##// END OF EJS Templates
Fix a number of bugs with %history, add proper tests....
Fernando Perez -
Show More
@@ -1,281 +1,258 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
3 """ History related magics and functionality """
2 """ History related magics and functionality """
4
3
5 # Stdlib imports
4 # Stdlib imports
6 import fnmatch
5 import fnmatch
7 import os
6 import os
8
7
9 # IPython imports
8 # IPython imports
10 from IPython.genutils import Term, ask_yes_no
9 from IPython.genutils import Term, ask_yes_no, warn
11 import IPython.ipapi
10 import IPython.ipapi
12
11
13 def magic_history(self, parameter_s = ''):
12 def magic_history(self, parameter_s = ''):
14 """Print input history (_i<n> variables), with most recent last.
13 """Print input history (_i<n> variables), with most recent last.
15
14
16 %history -> print at most 40 inputs (some may be multi-line)\\
15 %history -> print at most 40 inputs (some may be multi-line)\\
17 %history n -> print at most n inputs\\
16 %history n -> print at most n inputs\\
18 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
17 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
19
18
20 Each input's number <n> is shown, and is accessible as the
19 Each input's number <n> is shown, and is accessible as the
21 automatically generated variable _i<n>. Multi-line statements are
20 automatically generated variable _i<n>. Multi-line statements are
22 printed starting at a new line for easy copy/paste.
21 printed starting at a new line for easy copy/paste.
23
22
24
23
25 Options:
24 Options:
26
25
27 -n: do NOT print line numbers. This is useful if you want to get a
26 -n: do NOT print line numbers. This is useful if you want to get a
28 printout of many lines which can be directly pasted into a text
27 printout of many lines which can be directly pasted into a text
29 editor.
28 editor.
30
29
31 This feature is only available if numbered prompts are in use.
30 This feature is only available if numbered prompts are in use.
32
31
33 -t: (default) print the 'translated' history, as IPython understands it.
32 -t: (default) print the 'translated' history, as IPython understands it.
34 IPython filters your input and converts it all into valid Python source
33 IPython filters your input and converts it all into valid Python source
35 before executing it (things like magics or aliases are turned into
34 before executing it (things like magics or aliases are turned into
36 function calls, for example). With this option, you'll see the native
35 function calls, for example). With this option, you'll see the native
37 history instead of the user-entered version: '%cd /' will be seen as
36 history instead of the user-entered version: '%cd /' will be seen as
38 '_ip.magic("%cd /")' instead of '%cd /'.
37 '_ip.magic("%cd /")' instead of '%cd /'.
39
38
40 -r: print the 'raw' history, i.e. the actual commands you typed.
39 -r: print the 'raw' history, i.e. the actual commands you typed.
41
40
42 -g: treat the arg as a pattern to grep for in (full) history.
41 -g: treat the arg as a pattern to grep for in (full) history.
43 This includes the "shadow history" (almost all commands ever written).
42 This includes the "shadow history" (almost all commands ever written).
44 Use '%hist -g' to show full shadow history (may be very long).
43 Use '%hist -g' to show full shadow history (may be very long).
45 In shadow history, every index nuwber starts with 0.
44 In shadow history, every index nuwber starts with 0.
46
45
47 -f FILENAME: instead of printing the output to the screen, redirect it to
46 -f FILENAME: instead of printing the output to the screen, redirect it to
48 the given file. The file is always overwritten, though IPython asks for
47 the given file. The file is always overwritten, though IPython asks for
49 confirmation first if it already exists.
48 confirmation first if it already exists.
50
51
52 """
49 """
53
50
54 ip = self.api
51 ip = self.api
55 shell = self.shell
52 shell = self.shell
56 if not shell.outputcache.do_full_cache:
53 if not shell.outputcache.do_full_cache:
57 print 'This feature is only available if numbered prompts are in use.'
54 print 'This feature is only available if numbered prompts are in use.'
58 return
55 return
59 opts,args = self.parse_options(parameter_s,'gntsrf:',mode='list')
56 opts,args = self.parse_options(parameter_s,'gntsrf:',mode='list')
60
57
61 # Check if output to specific file was requested.
58 # Check if output to specific file was requested.
62 try:
59 try:
63 outfname = opts['f']
60 outfname = opts['f']
64 except KeyError:
61 except KeyError:
65 outfile = Term.cout
62 outfile = Term.cout # default
66 # We don't want to close stdout at the end!
63 # We don't want to close stdout at the end!
67 close_at_end = False
64 close_at_end = False
68 else:
65 else:
69 if os.path.exists(outfname):
66 if os.path.exists(outfname):
70 ans = ask_yes_no("File %r exists. Overwrite?" % outfname)
67 if not ask_yes_no("File %r exists. Overwrite?" % outfname):
71 if not ans:
72 print 'Aborting.'
68 print 'Aborting.'
73 return
69 return
74 else:
75 outfile = open(outfname,'w')
76 close_at_end = True
77
78
70
79 if opts.has_key('t'):
71 outfile = open(outfname,'w')
72 close_at_end = True
73
74 if 't' in opts:
80 input_hist = shell.input_hist
75 input_hist = shell.input_hist
81 elif opts.has_key('r'):
76 elif 'r' in opts:
82 input_hist = shell.input_hist_raw
77 input_hist = shell.input_hist_raw
83 else:
78 else:
84 input_hist = shell.input_hist
79 input_hist = shell.input_hist
85
80
86
87 default_length = 40
81 default_length = 40
88 pattern = None
82 pattern = None
89 if opts.has_key('g'):
83 if 'g' in opts:
90 init = 1
84 init = 1
91 final = len(input_hist)
85 final = len(input_hist)
92 parts = parameter_s.split(None,1)
86 parts = parameter_s.split(None,1)
93 if len(parts) == 1:
87 if len(parts) == 1:
94 parts += '*'
88 parts += '*'
95 head, pattern = parts
89 head, pattern = parts
96 pattern = "*" + pattern + "*"
90 pattern = "*" + pattern + "*"
97 elif len(args) == 0:
91 elif len(args) == 0:
98 final = len(input_hist)
92 final = len(input_hist)
99 init = max(1,final-default_length)
93 init = max(1,final-default_length)
100 elif len(args) == 1:
94 elif len(args) == 1:
101 final = len(input_hist)
95 final = len(input_hist)
102 init = max(1,final-int(args[0]))
96 init = max(1,final-int(args[0]))
103 elif len(args) == 2:
97 elif len(args) == 2:
104 init,final = map(int,args)
98 init,final = map(int,args)
105 else:
99 else:
106 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
100 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
107 print self.magic_hist.__doc__
101 print self.magic_hist.__doc__
108 return
102 return
109 width = len(str(final))
103 width = len(str(final))
110 line_sep = ['','\n']
104 line_sep = ['','\n']
111 print_nums = not opts.has_key('n')
105 print_nums = not opts.has_key('n')
112
106
113 found = False
107 found = False
114 if pattern is not None:
108 if pattern is not None:
115 sh = ip.IP.shadowhist.all()
109 sh = ip.IP.shadowhist.all()
116 for idx, s in sh:
110 for idx, s in sh:
117 if fnmatch.fnmatch(s, pattern):
111 if fnmatch.fnmatch(s, pattern):
118 print "0%d: %s" %(idx, s)
112 print "0%d: %s" %(idx, s)
119 found = True
113 found = True
120
114
121 if found:
115 if found:
122 print "==="
116 print "==="
123 print "shadow history ends, fetch by %rep <number> (must start with 0)"
117 print "shadow history ends, fetch by %rep <number> (must start with 0)"
124 print "=== start of normal history ==="
118 print "=== start of normal history ==="
125
119
126 for in_num in range(init,final):
120 for in_num in range(init,final):
127 inline = input_hist[in_num]
121 inline = input_hist[in_num]
128 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
122 if pattern is not None and not fnmatch.fnmatch(inline, pattern):
129 continue
123 continue
130
124
131 multiline = int(inline.count('\n') > 1)
125 multiline = int(inline.count('\n') > 1)
132 if print_nums:
126 if print_nums:
133 print >> outfile, \
127 print >> outfile, \
134 '%s:%s' % (str(in_num).ljust(width),line_sep[multiline]),
128 '%s:%s' % (str(in_num).ljust(width),line_sep[multiline]),
135 print >> outfile, inline,
129 print >> outfile, inline,
136
130
137 if close_at_end:
131 if close_at_end:
138 outfile.close()
132 outfile.close()
139
133
140
134
141
142 def magic_hist(self, parameter_s=''):
135 def magic_hist(self, parameter_s=''):
143 """Alternate name for %history."""
136 """Alternate name for %history."""
144 return self.magic_history(parameter_s)
137 return self.magic_history(parameter_s)
145
138
146
139
147
148 def rep_f(self, arg):
140 def rep_f(self, arg):
149 r""" Repeat a command, or get command to input line for editing
141 r""" Repeat a command, or get command to input line for editing
150
142
151 - %rep (no arguments):
143 - %rep (no arguments):
152
144
153 Place a string version of last computation result (stored in the special '_'
145 Place a string version of last computation result (stored in the special '_'
154 variable) to the next input prompt. Allows you to create elaborate command
146 variable) to the next input prompt. Allows you to create elaborate command
155 lines without using copy-paste::
147 lines without using copy-paste::
156
148
157 $ l = ["hei", "vaan"]
149 $ l = ["hei", "vaan"]
158 $ "".join(l)
150 $ "".join(l)
159 ==> heivaan
151 ==> heivaan
160 $ %rep
152 $ %rep
161 $ heivaan_ <== cursor blinking
153 $ heivaan_ <== cursor blinking
162
154
163 %rep 45
155 %rep 45
164
156
165 Place history line 45 to next input prompt. Use %hist to find out the
157 Place history line 45 to next input prompt. Use %hist to find out the
166 number.
158 number.
167
159
168 %rep 1-4 6-7 3
160 %rep 1-4 6-7 3
169
161
170 Repeat the specified lines immediately. Input slice syntax is the same as
162 Repeat the specified lines immediately. Input slice syntax is the same as
171 in %macro and %save.
163 in %macro and %save.
172
164
173 %rep foo
165 %rep foo
174
166
175 Place the most recent line that has the substring "foo" to next input.
167 Place the most recent line that has the substring "foo" to next input.
176 (e.g. 'svn ci -m foobar').
168 (e.g. 'svn ci -m foobar').
177
178 """
169 """
179
170
180
181 opts,args = self.parse_options(arg,'',mode='list')
171 opts,args = self.parse_options(arg,'',mode='list')
182 ip = self.api
172 ip = self.api
183 if not args:
173 if not args:
184 ip.set_next_input(str(ip.user_ns["_"]))
174 ip.set_next_input(str(ip.user_ns["_"]))
185 return
175 return
186
176
187 if len(args) == 1 and not '-' in args[0]:
177 if len(args) == 1 and not '-' in args[0]:
188 arg = args[0]
178 arg = args[0]
189 if len(arg) > 1 and arg.startswith('0'):
179 if len(arg) > 1 and arg.startswith('0'):
190 # get from shadow hist
180 # get from shadow hist
191 num = int(arg[1:])
181 num = int(arg[1:])
192 line = self.shadowhist.get(num)
182 line = self.shadowhist.get(num)
193 ip.set_next_input(str(line))
183 ip.set_next_input(str(line))
194 return
184 return
195 try:
185 try:
196 num = int(args[0])
186 num = int(args[0])
197 ip.set_next_input(str(ip.IP.input_hist_raw[num]).rstrip())
187 ip.set_next_input(str(ip.IP.input_hist_raw[num]).rstrip())
198 return
188 return
199 except ValueError:
189 except ValueError:
200 pass
190 pass
201
191
202 for h in reversed(self.shell.input_hist_raw):
192 for h in reversed(self.shell.input_hist_raw):
203 if 'rep' in h:
193 if 'rep' in h:
204 continue
194 continue
205 if fnmatch.fnmatch(h,'*' + arg + '*'):
195 if fnmatch.fnmatch(h,'*' + arg + '*'):
206 ip.set_next_input(str(h).rstrip())
196 ip.set_next_input(str(h).rstrip())
207 return
197 return
208
198
209
210 try:
199 try:
211 lines = self.extract_input_slices(args, True)
200 lines = self.extract_input_slices(args, True)
212 print "lines",lines
201 print "lines",lines
213 ip.runlines(lines)
202 ip.runlines(lines)
214 except ValueError:
203 except ValueError:
215 print "Not found in recent history:", args
204 print "Not found in recent history:", args
216
205
217
206
218
219 _sentinel = object()
207 _sentinel = object()
220
208
221 class ShadowHist:
209 class ShadowHist:
222 def __init__(self,db):
210 def __init__(self,db):
223 # cmd => idx mapping
211 # cmd => idx mapping
224 self.curidx = 0
212 self.curidx = 0
225 self.db = db
213 self.db = db
226 self.disabled = False
214 self.disabled = False
227
215
228 def inc_idx(self):
216 def inc_idx(self):
229 idx = self.db.get('shadowhist_idx', 1)
217 idx = self.db.get('shadowhist_idx', 1)
230 self.db['shadowhist_idx'] = idx + 1
218 self.db['shadowhist_idx'] = idx + 1
231 return idx
219 return idx
232
220
233 def add(self, ent):
221 def add(self, ent):
234 if self.disabled:
222 if self.disabled:
235 return
223 return
236 try:
224 try:
237 old = self.db.hget('shadowhist', ent, _sentinel)
225 old = self.db.hget('shadowhist', ent, _sentinel)
238 if old is not _sentinel:
226 if old is not _sentinel:
239 return
227 return
240 newidx = self.inc_idx()
228 newidx = self.inc_idx()
241 #print "new",newidx # dbg
229 #print "new",newidx # dbg
242 self.db.hset('shadowhist',ent, newidx)
230 self.db.hset('shadowhist',ent, newidx)
243 except:
231 except:
244 IPython.ipapi.get().IP.showtraceback()
232 IPython.ipapi.get().IP.showtraceback()
245 print "WARNING: disabling shadow history"
233 print "WARNING: disabling shadow history"
246 self.disabled = True
234 self.disabled = True
247
235
248 def all(self):
236 def all(self):
249 d = self.db.hdict('shadowhist')
237 d = self.db.hdict('shadowhist')
250 items = [(i,s) for (s,i) in d.items()]
238 items = [(i,s) for (s,i) in d.items()]
251 items.sort()
239 items.sort()
252 return items
240 return items
253
241
254 def get(self, idx):
242 def get(self, idx):
255 all = self.all()
243 all = self.all()
256
244
257 for k, v in all:
245 for k, v in all:
258 #print k,v
246 #print k,v
259 if k == idx:
247 if k == idx:
260 return v
248 return v
261
249
262 def test_shist():
263 from IPython.Extensions import pickleshare
264 db = pickleshare.PickleShareDB('~/shist')
265 s = ShadowHist(db)
266 s.add('hello')
267 s.add('world')
268 s.add('hello')
269 s.add('hello')
270 s.add('karhu')
271 print "all",s.all()
272 print s.get(2)
273
250
274 def init_ipython(ip):
251 def init_ipython(ip):
252 import ipy_completers
253
275 ip.expose_magic("rep",rep_f)
254 ip.expose_magic("rep",rep_f)
276 ip.expose_magic("hist",magic_hist)
255 ip.expose_magic("hist",magic_hist)
277 ip.expose_magic("history",magic_history)
256 ip.expose_magic("history",magic_history)
278
257
279 import ipy_completers
280 ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
258 ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
281 #test_shist()
@@ -1,805 +1,820 b''
1 """Nose Plugin that supports IPython doctests.
1 """Nose Plugin that supports IPython doctests.
2
2
3 Limitations:
3 Limitations:
4
4
5 - When generating examples for use as doctests, make sure that you have
5 - When generating examples for use as doctests, make sure that you have
6 pretty-printing OFF. This can be done either by starting ipython with the
6 pretty-printing OFF. This can be done either by starting ipython with the
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
8 interactively disabling it with %Pprint. This is required so that IPython
8 interactively disabling it with %Pprint. This is required so that IPython
9 output matches that of normal Python, which is used by doctest for internal
9 output matches that of normal Python, which is used by doctest for internal
10 execution.
10 execution.
11
11
12 - Do not rely on specific prompt numbers for results (such as using
12 - Do not rely on specific prompt numbers for results (such as using
13 '_34==True', for example). For IPython tests run via an external process the
13 '_34==True', for example). For IPython tests run via an external process the
14 prompt numbers may be different, and IPython tests run as normal python code
14 prompt numbers may be different, and IPython tests run as normal python code
15 won't even have these special _NN variables set at all.
15 won't even have these special _NN variables set at all.
16 """
16 """
17
17
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Module imports
21
21
22 # From the standard library
22 # From the standard library
23 import __builtin__
23 import __builtin__
24 import commands
24 import commands
25 import doctest
25 import doctest
26 import inspect
26 import inspect
27 import logging
27 import logging
28 import os
28 import os
29 import re
29 import re
30 import sys
30 import sys
31 import traceback
31 import traceback
32 import unittest
32 import unittest
33
33
34 from inspect import getmodule
34 from inspect import getmodule
35 from StringIO import StringIO
35 from StringIO import StringIO
36
36
37 # We are overriding the default doctest runner, so we need to import a few
37 # We are overriding the default doctest runner, so we need to import a few
38 # things from doctest directly
38 # things from doctest directly
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
40 _unittest_reportflags, DocTestRunner,
40 _unittest_reportflags, DocTestRunner,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
42 _exception_traceback,
42 _exception_traceback,
43 linecache)
43 linecache)
44
44
45 # Third-party modules
45 # Third-party modules
46 import nose.core
46 import nose.core
47
47
48 from nose.plugins import doctests, Plugin
48 from nose.plugins import doctests, Plugin
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
50
50
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52 # Module globals and other constants
52 # Module globals and other constants
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 ###########################################################################
56 ###########################################################################
57 # *** HACK ***
57 # *** HACK ***
58 # We must start our own ipython object and heavily muck with it so that all the
58 # We must start our own ipython object and heavily muck with it so that all the
59 # modifications IPython makes to system behavior don't send the doctest
59 # modifications IPython makes to system behavior don't send the doctest
60 # machinery into a fit. This code should be considered a gross hack, but it
60 # machinery into a fit. This code should be considered a gross hack, but it
61 # gets the job done.
61 # gets the job done.
62
62
63
63
64 # Hack to modify the %run command so we can sync the user's namespace with the
64 # Hack to modify the %run command so we can sync the user's namespace with the
65 # test globals. Once we move over to a clean magic system, this will be done
65 # test globals. Once we move over to a clean magic system, this will be done
66 # with much less ugliness.
66 # with much less ugliness.
67
67
68 class py_file_finder(object):
69 def __init__(self,test_filename):
70 self.test_filename = test_filename
71
72 def __call__(self,name):
73 from IPython.genutils import get_py_filename
74 try:
75 get_py_filename(name)
76 except IOError:
77 test_dir = os.path.dirname(self.test_filename)
78 new_path = os.path.join(test_dir,name)
79 return get_py_filename(new_path)
80
81
68 def _run_ns_sync(self,arg_s,runner=None):
82 def _run_ns_sync(self,arg_s,runner=None):
69 """Modified version of %run that syncs testing namespaces.
83 """Modified version of %run that syncs testing namespaces.
70
84
71 This is strictly needed for running doctests that call %run.
85 This is strictly needed for running doctests that call %run.
72 """
86 """
73
87
74 out = _ip.IP.magic_run_ori(arg_s,runner)
88 finder = py_file_finder(_run_ns_sync.test_filename)
89 out = _ip.IP.magic_run_ori(arg_s,runner,finder)
75 _run_ns_sync.test_globs.update(_ip.user_ns)
90 _run_ns_sync.test_globs.update(_ip.user_ns)
76 return out
91 return out
77
92
78
93
79 class ipnsdict(dict):
94 class ipnsdict(dict):
80 """A special subclass of dict for use as an IPython namespace in doctests.
95 """A special subclass of dict for use as an IPython namespace in doctests.
81
96
82 This subclass adds a simple checkpointing capability so that when testing
97 This subclass adds a simple checkpointing capability so that when testing
83 machinery clears it (we use it as the test execution context), it doesn't
98 machinery clears it (we use it as the test execution context), it doesn't
84 get completely destroyed.
99 get completely destroyed.
85 """
100 """
86
101
87 def __init__(self,*a):
102 def __init__(self,*a):
88 dict.__init__(self,*a)
103 dict.__init__(self,*a)
89 self._savedict = {}
104 self._savedict = {}
90
105
91 def clear(self):
106 def clear(self):
92 dict.clear(self)
107 dict.clear(self)
93 self.update(self._savedict)
108 self.update(self._savedict)
94
109
95 def _checkpoint(self):
110 def _checkpoint(self):
96 self._savedict.clear()
111 self._savedict.clear()
97 self._savedict.update(self)
112 self._savedict.update(self)
98
113
99 def update(self,other):
114 def update(self,other):
100 self._checkpoint()
115 self._checkpoint()
101 dict.update(self,other)
116 dict.update(self,other)
102 # If '_' is in the namespace, python won't set it when executing code,
117 # If '_' is in the namespace, python won't set it when executing code,
103 # and we have examples that test it. So we ensure that the namespace
118 # and we have examples that test it. So we ensure that the namespace
104 # is always 'clean' of it before it's used for test code execution.
119 # is always 'clean' of it before it's used for test code execution.
105 self.pop('_',None)
120 self.pop('_',None)
106
121
107
122
108 def start_ipython():
123 def start_ipython():
109 """Start a global IPython shell, which we need for IPython-specific syntax.
124 """Start a global IPython shell, which we need for IPython-specific syntax.
110 """
125 """
111 import new
126 import new
112
127
113 import IPython
128 import IPython
114
129
115 def xsys(cmd):
130 def xsys(cmd):
116 """Execute a command and print its output.
131 """Execute a command and print its output.
117
132
118 This is just a convenience function to replace the IPython system call
133 This is just a convenience function to replace the IPython system call
119 with one that is more doctest-friendly.
134 with one that is more doctest-friendly.
120 """
135 """
121 cmd = _ip.IP.var_expand(cmd,depth=1)
136 cmd = _ip.IP.var_expand(cmd,depth=1)
122 sys.stdout.write(commands.getoutput(cmd))
137 sys.stdout.write(commands.getoutput(cmd))
123 sys.stdout.flush()
138 sys.stdout.flush()
124
139
125 # Store certain global objects that IPython modifies
140 # Store certain global objects that IPython modifies
126 _displayhook = sys.displayhook
141 _displayhook = sys.displayhook
127 _excepthook = sys.excepthook
142 _excepthook = sys.excepthook
128 _main = sys.modules.get('__main__')
143 _main = sys.modules.get('__main__')
129
144
130 # Start IPython instance. We customize it to start with minimal frills.
145 # Start IPython instance. We customize it to start with minimal frills.
131 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
146 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
132
147 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
133 IPython.Shell.IPShell(['--classic','--noterm_title'],
134 user_ns,global_ns)
148 user_ns,global_ns)
135
149
136 # Deactivate the various python system hooks added by ipython for
150 # Deactivate the various python system hooks added by ipython for
137 # interactive convenience so we don't confuse the doctest system
151 # interactive convenience so we don't confuse the doctest system
138 sys.modules['__main__'] = _main
152 sys.modules['__main__'] = _main
139 sys.displayhook = _displayhook
153 sys.displayhook = _displayhook
140 sys.excepthook = _excepthook
154 sys.excepthook = _excepthook
141
155
142 # So that ipython magics and aliases can be doctested (they work by making
156 # So that ipython magics and aliases can be doctested (they work by making
143 # a call into a global _ip object)
157 # a call into a global _ip object)
144 _ip = IPython.ipapi.get()
158 _ip = IPython.ipapi.get()
145 __builtin__._ip = _ip
159 __builtin__._ip = _ip
146
160
147 # Modify the IPython system call with one that uses getoutput, so that we
161 # Modify the IPython system call with one that uses getoutput, so that we
148 # can capture subcommands and print them to Python's stdout, otherwise the
162 # can capture subcommands and print them to Python's stdout, otherwise the
149 # doctest machinery would miss them.
163 # doctest machinery would miss them.
150 _ip.system = xsys
164 _ip.system = xsys
151
165
152 # Also patch our %run function in.
166 # Also patch our %run function in.
153 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
167 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
154 _ip.IP.magic_run_ori = _ip.IP.magic_run
168 _ip.IP.magic_run_ori = _ip.IP.magic_run
155 _ip.IP.magic_run = im
169 _ip.IP.magic_run = im
156
170
157 # The start call MUST be made here. I'm not sure yet why it doesn't work if
171 # The start call MUST be made here. I'm not sure yet why it doesn't work if
158 # it is made later, at plugin initialization time, but in all my tests, that's
172 # it is made later, at plugin initialization time, but in all my tests, that's
159 # the case.
173 # the case.
160 start_ipython()
174 start_ipython()
161
175
162 # *** END HACK ***
176 # *** END HACK ***
163 ###########################################################################
177 ###########################################################################
164
178
165 # Classes and functions
179 # Classes and functions
166
180
167 def is_extension_module(filename):
181 def is_extension_module(filename):
168 """Return whether the given filename is an extension module.
182 """Return whether the given filename is an extension module.
169
183
170 This simply checks that the extension is either .so or .pyd.
184 This simply checks that the extension is either .so or .pyd.
171 """
185 """
172 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
186 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
173
187
174
188
175 class nodoc(object):
189 class nodoc(object):
176 def __init__(self,obj):
190 def __init__(self,obj):
177 self.obj = obj
191 self.obj = obj
178
192
179 def __getattribute__(self,key):
193 def __getattribute__(self,key):
180 if key == '__doc__':
194 if key == '__doc__':
181 return None
195 return None
182 else:
196 else:
183 return getattr(object.__getattribute__(self,'obj'),key)
197 return getattr(object.__getattribute__(self,'obj'),key)
184
198
185 # Modified version of the one in the stdlib, that fixes a python bug (doctests
199 # Modified version of the one in the stdlib, that fixes a python bug (doctests
186 # not found in extension modules, http://bugs.python.org/issue3158)
200 # not found in extension modules, http://bugs.python.org/issue3158)
187 class DocTestFinder(doctest.DocTestFinder):
201 class DocTestFinder(doctest.DocTestFinder):
188
202
189 def _from_module(self, module, object):
203 def _from_module(self, module, object):
190 """
204 """
191 Return true if the given object is defined in the given
205 Return true if the given object is defined in the given
192 module.
206 module.
193 """
207 """
194 if module is None:
208 if module is None:
195 return True
209 return True
196 elif inspect.isfunction(object):
210 elif inspect.isfunction(object):
197 return module.__dict__ is object.func_globals
211 return module.__dict__ is object.func_globals
198 elif inspect.isbuiltin(object):
212 elif inspect.isbuiltin(object):
199 return module.__name__ == object.__module__
213 return module.__name__ == object.__module__
200 elif inspect.isclass(object):
214 elif inspect.isclass(object):
201 return module.__name__ == object.__module__
215 return module.__name__ == object.__module__
202 elif inspect.ismethod(object):
216 elif inspect.ismethod(object):
203 # This one may be a bug in cython that fails to correctly set the
217 # This one may be a bug in cython that fails to correctly set the
204 # __module__ attribute of methods, but since the same error is easy
218 # __module__ attribute of methods, but since the same error is easy
205 # to make by extension code writers, having this safety in place
219 # to make by extension code writers, having this safety in place
206 # isn't such a bad idea
220 # isn't such a bad idea
207 return module.__name__ == object.im_class.__module__
221 return module.__name__ == object.im_class.__module__
208 elif inspect.getmodule(object) is not None:
222 elif inspect.getmodule(object) is not None:
209 return module is inspect.getmodule(object)
223 return module is inspect.getmodule(object)
210 elif hasattr(object, '__module__'):
224 elif hasattr(object, '__module__'):
211 return module.__name__ == object.__module__
225 return module.__name__ == object.__module__
212 elif isinstance(object, property):
226 elif isinstance(object, property):
213 return True # [XX] no way not be sure.
227 return True # [XX] no way not be sure.
214 else:
228 else:
215 raise ValueError("object must be a class or function")
229 raise ValueError("object must be a class or function")
216
230
217 def _find(self, tests, obj, name, module, source_lines, globs, seen):
231 def _find(self, tests, obj, name, module, source_lines, globs, seen):
218 """
232 """
219 Find tests for the given object and any contained objects, and
233 Find tests for the given object and any contained objects, and
220 add them to `tests`.
234 add them to `tests`.
221 """
235 """
222
236
223 if hasattr(obj,"skip_doctest"):
237 if hasattr(obj,"skip_doctest"):
224 #print 'SKIPPING DOCTEST FOR:',obj # dbg
238 #print 'SKIPPING DOCTEST FOR:',obj # dbg
225 obj = nodoc(obj)
239 obj = nodoc(obj)
226
240
227 doctest.DocTestFinder._find(self,tests, obj, name, module,
241 doctest.DocTestFinder._find(self,tests, obj, name, module,
228 source_lines, globs, seen)
242 source_lines, globs, seen)
229
243
230 # Below we re-run pieces of the above method with manual modifications,
244 # Below we re-run pieces of the above method with manual modifications,
231 # because the original code is buggy and fails to correctly identify
245 # because the original code is buggy and fails to correctly identify
232 # doctests in extension modules.
246 # doctests in extension modules.
233
247
234 # Local shorthands
248 # Local shorthands
235 from inspect import isroutine, isclass, ismodule
249 from inspect import isroutine, isclass, ismodule
236
250
237 # Look for tests in a module's contained objects.
251 # Look for tests in a module's contained objects.
238 if inspect.ismodule(obj) and self._recurse:
252 if inspect.ismodule(obj) and self._recurse:
239 for valname, val in obj.__dict__.items():
253 for valname, val in obj.__dict__.items():
240 valname1 = '%s.%s' % (name, valname)
254 valname1 = '%s.%s' % (name, valname)
241 if ( (isroutine(val) or isclass(val))
255 if ( (isroutine(val) or isclass(val))
242 and self._from_module(module, val) ):
256 and self._from_module(module, val) ):
243
257
244 self._find(tests, val, valname1, module, source_lines,
258 self._find(tests, val, valname1, module, source_lines,
245 globs, seen)
259 globs, seen)
246
260
247 # Look for tests in a class's contained objects.
261 # Look for tests in a class's contained objects.
248 if inspect.isclass(obj) and self._recurse:
262 if inspect.isclass(obj) and self._recurse:
249 #print 'RECURSE into class:',obj # dbg
263 #print 'RECURSE into class:',obj # dbg
250 for valname, val in obj.__dict__.items():
264 for valname, val in obj.__dict__.items():
251 # Special handling for staticmethod/classmethod.
265 # Special handling for staticmethod/classmethod.
252 if isinstance(val, staticmethod):
266 if isinstance(val, staticmethod):
253 val = getattr(obj, valname)
267 val = getattr(obj, valname)
254 if isinstance(val, classmethod):
268 if isinstance(val, classmethod):
255 val = getattr(obj, valname).im_func
269 val = getattr(obj, valname).im_func
256
270
257 # Recurse to methods, properties, and nested classes.
271 # Recurse to methods, properties, and nested classes.
258 if ((inspect.isfunction(val) or inspect.isclass(val) or
272 if ((inspect.isfunction(val) or inspect.isclass(val) or
259 inspect.ismethod(val) or
273 inspect.ismethod(val) or
260 isinstance(val, property)) and
274 isinstance(val, property)) and
261 self._from_module(module, val)):
275 self._from_module(module, val)):
262 valname = '%s.%s' % (name, valname)
276 valname = '%s.%s' % (name, valname)
263 self._find(tests, val, valname, module, source_lines,
277 self._find(tests, val, valname, module, source_lines,
264 globs, seen)
278 globs, seen)
265
279
266
280
267 class IPDoctestOutputChecker(doctest.OutputChecker):
281 class IPDoctestOutputChecker(doctest.OutputChecker):
268 """Second-chance checker with support for random tests.
282 """Second-chance checker with support for random tests.
269
283
270 If the default comparison doesn't pass, this checker looks in the expected
284 If the default comparison doesn't pass, this checker looks in the expected
271 output string for flags that tell us to ignore the output.
285 output string for flags that tell us to ignore the output.
272 """
286 """
273
287
274 random_re = re.compile(r'#\s*random\s+')
288 random_re = re.compile(r'#\s*random\s+')
275
289
276 def check_output(self, want, got, optionflags):
290 def check_output(self, want, got, optionflags):
277 """Check output, accepting special markers embedded in the output.
291 """Check output, accepting special markers embedded in the output.
278
292
279 If the output didn't pass the default validation but the special string
293 If the output didn't pass the default validation but the special string
280 '#random' is included, we accept it."""
294 '#random' is included, we accept it."""
281
295
282 # Let the original tester verify first, in case people have valid tests
296 # Let the original tester verify first, in case people have valid tests
283 # that happen to have a comment saying '#random' embedded in.
297 # that happen to have a comment saying '#random' embedded in.
284 ret = doctest.OutputChecker.check_output(self, want, got,
298 ret = doctest.OutputChecker.check_output(self, want, got,
285 optionflags)
299 optionflags)
286 if not ret and self.random_re.search(want):
300 if not ret and self.random_re.search(want):
287 #print >> sys.stderr, 'RANDOM OK:',want # dbg
301 #print >> sys.stderr, 'RANDOM OK:',want # dbg
288 return True
302 return True
289
303
290 return ret
304 return ret
291
305
292
306
293 class DocTestCase(doctests.DocTestCase):
307 class DocTestCase(doctests.DocTestCase):
294 """Proxy for DocTestCase: provides an address() method that
308 """Proxy for DocTestCase: provides an address() method that
295 returns the correct address for the doctest case. Otherwise
309 returns the correct address for the doctest case. Otherwise
296 acts as a proxy to the test case. To provide hints for address(),
310 acts as a proxy to the test case. To provide hints for address(),
297 an obj may also be passed -- this will be used as the test object
311 an obj may also be passed -- this will be used as the test object
298 for purposes of determining the test address, if it is provided.
312 for purposes of determining the test address, if it is provided.
299 """
313 """
300
314
301 # Note: this method was taken from numpy's nosetester module.
315 # Note: this method was taken from numpy's nosetester module.
302
316
303 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
317 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
304 # its constructor that blocks non-default arguments from being passed
318 # its constructor that blocks non-default arguments from being passed
305 # down into doctest.DocTestCase
319 # down into doctest.DocTestCase
306
320
307 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
321 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
308 checker=None, obj=None, result_var='_'):
322 checker=None, obj=None, result_var='_'):
309 self._result_var = result_var
323 self._result_var = result_var
310 doctests.DocTestCase.__init__(self, test,
324 doctests.DocTestCase.__init__(self, test,
311 optionflags=optionflags,
325 optionflags=optionflags,
312 setUp=setUp, tearDown=tearDown,
326 setUp=setUp, tearDown=tearDown,
313 checker=checker)
327 checker=checker)
314 # Now we must actually copy the original constructor from the stdlib
328 # Now we must actually copy the original constructor from the stdlib
315 # doctest class, because we can't call it directly and a bug in nose
329 # doctest class, because we can't call it directly and a bug in nose
316 # means it never gets passed the right arguments.
330 # means it never gets passed the right arguments.
317
331
318 self._dt_optionflags = optionflags
332 self._dt_optionflags = optionflags
319 self._dt_checker = checker
333 self._dt_checker = checker
320 self._dt_test = test
334 self._dt_test = test
321 self._dt_setUp = setUp
335 self._dt_setUp = setUp
322 self._dt_tearDown = tearDown
336 self._dt_tearDown = tearDown
323
337
324 # XXX - store this runner once in the object!
338 # XXX - store this runner once in the object!
325 runner = IPDocTestRunner(optionflags=optionflags,
339 runner = IPDocTestRunner(optionflags=optionflags,
326 checker=checker, verbose=False)
340 checker=checker, verbose=False)
327 self._dt_runner = runner
341 self._dt_runner = runner
328
342
329
343
330 # Each doctest should remember what directory it was loaded from...
344 # Each doctest should remember what directory it was loaded from...
331 self._ori_dir = os.getcwd()
345 self._ori_dir = os.getcwd()
332
346
333 # Modified runTest from the default stdlib
347 # Modified runTest from the default stdlib
334 def runTest(self):
348 def runTest(self):
335 test = self._dt_test
349 test = self._dt_test
336 runner = self._dt_runner
350 runner = self._dt_runner
337
351
338 old = sys.stdout
352 old = sys.stdout
339 new = StringIO()
353 new = StringIO()
340 optionflags = self._dt_optionflags
354 optionflags = self._dt_optionflags
341
355
342 if not (optionflags & REPORTING_FLAGS):
356 if not (optionflags & REPORTING_FLAGS):
343 # The option flags don't include any reporting flags,
357 # The option flags don't include any reporting flags,
344 # so add the default reporting flags
358 # so add the default reporting flags
345 optionflags |= _unittest_reportflags
359 optionflags |= _unittest_reportflags
346
360
347 try:
361 try:
348 # Save our current directory and switch out to the one where the
362 # Save our current directory and switch out to the one where the
349 # test was originally created, in case another doctest did a
363 # test was originally created, in case another doctest did a
350 # directory change. We'll restore this in the finally clause.
364 # directory change. We'll restore this in the finally clause.
351 curdir = os.getcwd()
365 curdir = os.getcwd()
352 os.chdir(self._ori_dir)
366 os.chdir(self._ori_dir)
353
367
354 runner.DIVIDER = "-"*70
368 runner.DIVIDER = "-"*70
355 failures, tries = runner.run(test,out=new.write,
369 failures, tries = runner.run(test,out=new.write,
356 clear_globs=False)
370 clear_globs=False)
357 finally:
371 finally:
358 sys.stdout = old
372 sys.stdout = old
359 os.chdir(curdir)
373 os.chdir(curdir)
360
374
361 if failures:
375 if failures:
362 raise self.failureException(self.format_failure(new.getvalue()))
376 raise self.failureException(self.format_failure(new.getvalue()))
363
377
364 def setUp(self):
378 def setUp(self):
365 """Modified test setup that syncs with ipython namespace"""
379 """Modified test setup that syncs with ipython namespace"""
366
380
367 if isinstance(self._dt_test.examples[0],IPExample):
381 if isinstance(self._dt_test.examples[0],IPExample):
368 # for IPython examples *only*, we swap the globals with the ipython
382 # for IPython examples *only*, we swap the globals with the ipython
369 # namespace, after updating it with the globals (which doctest
383 # namespace, after updating it with the globals (which doctest
370 # fills with the necessary info from the module being tested).
384 # fills with the necessary info from the module being tested).
371 _ip.IP.user_ns.update(self._dt_test.globs)
385 _ip.IP.user_ns.update(self._dt_test.globs)
372 self._dt_test.globs = _ip.IP.user_ns
386 self._dt_test.globs = _ip.IP.user_ns
373
387
374 doctests.DocTestCase.setUp(self)
388 doctests.DocTestCase.setUp(self)
375
389
376
390
377
391
378 # A simple subclassing of the original with a different class name, so we can
392 # A simple subclassing of the original with a different class name, so we can
379 # distinguish and treat differently IPython examples from pure python ones.
393 # distinguish and treat differently IPython examples from pure python ones.
380 class IPExample(doctest.Example): pass
394 class IPExample(doctest.Example): pass
381
395
382
396
383 class IPExternalExample(doctest.Example):
397 class IPExternalExample(doctest.Example):
384 """Doctest examples to be run in an external process."""
398 """Doctest examples to be run in an external process."""
385
399
386 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
400 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
387 options=None):
401 options=None):
388 # Parent constructor
402 # Parent constructor
389 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
403 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
390
404
391 # An EXTRA newline is needed to prevent pexpect hangs
405 # An EXTRA newline is needed to prevent pexpect hangs
392 self.source += '\n'
406 self.source += '\n'
393
407
394
408
395 class IPDocTestParser(doctest.DocTestParser):
409 class IPDocTestParser(doctest.DocTestParser):
396 """
410 """
397 A class used to parse strings containing doctest examples.
411 A class used to parse strings containing doctest examples.
398
412
399 Note: This is a version modified to properly recognize IPython input and
413 Note: This is a version modified to properly recognize IPython input and
400 convert any IPython examples into valid Python ones.
414 convert any IPython examples into valid Python ones.
401 """
415 """
402 # This regular expression is used to find doctest examples in a
416 # This regular expression is used to find doctest examples in a
403 # string. It defines three groups: `source` is the source code
417 # string. It defines three groups: `source` is the source code
404 # (including leading indentation and prompts); `indent` is the
418 # (including leading indentation and prompts); `indent` is the
405 # indentation of the first (PS1) line of the source code; and
419 # indentation of the first (PS1) line of the source code; and
406 # `want` is the expected output (including leading indentation).
420 # `want` is the expected output (including leading indentation).
407
421
408 # Classic Python prompts or default IPython ones
422 # Classic Python prompts or default IPython ones
409 _PS1_PY = r'>>>'
423 _PS1_PY = r'>>>'
410 _PS2_PY = r'\.\.\.'
424 _PS2_PY = r'\.\.\.'
411
425
412 _PS1_IP = r'In\ \[\d+\]:'
426 _PS1_IP = r'In\ \[\d+\]:'
413 _PS2_IP = r'\ \ \ \.\.\.+:'
427 _PS2_IP = r'\ \ \ \.\.\.+:'
414
428
415 _RE_TPL = r'''
429 _RE_TPL = r'''
416 # Source consists of a PS1 line followed by zero or more PS2 lines.
430 # Source consists of a PS1 line followed by zero or more PS2 lines.
417 (?P<source>
431 (?P<source>
418 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
432 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
419 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
433 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
420 \n? # a newline
434 \n? # a newline
421 # Want consists of any non-blank lines that do not start with PS1.
435 # Want consists of any non-blank lines that do not start with PS1.
422 (?P<want> (?:(?![ ]*$) # Not a blank line
436 (?P<want> (?:(?![ ]*$) # Not a blank line
423 (?![ ]*%s) # Not a line starting with PS1
437 (?![ ]*%s) # Not a line starting with PS1
424 (?![ ]*%s) # Not a line starting with PS2
438 (?![ ]*%s) # Not a line starting with PS2
425 .*$\n? # But any other line
439 .*$\n? # But any other line
426 )*)
440 )*)
427 '''
441 '''
428
442
429 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
443 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
430 re.MULTILINE | re.VERBOSE)
444 re.MULTILINE | re.VERBOSE)
431
445
432 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
446 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
433 re.MULTILINE | re.VERBOSE)
447 re.MULTILINE | re.VERBOSE)
434
448
435 # Mark a test as being fully random. In this case, we simply append the
449 # Mark a test as being fully random. In this case, we simply append the
436 # random marker ('#random') to each individual example's output. This way
450 # random marker ('#random') to each individual example's output. This way
437 # we don't need to modify any other code.
451 # we don't need to modify any other code.
438 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
452 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
439
453
440 # Mark tests to be executed in an external process - currently unsupported.
454 # Mark tests to be executed in an external process - currently unsupported.
441 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
455 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
442
456
443 def ip2py(self,source):
457 def ip2py(self,source):
444 """Convert input IPython source into valid Python."""
458 """Convert input IPython source into valid Python."""
445 out = []
459 out = []
446 newline = out.append
460 newline = out.append
447 for lnum,line in enumerate(source.splitlines()):
461 for lnum,line in enumerate(source.splitlines()):
448 newline(_ip.IP.prefilter(line,lnum>0))
462 newline(_ip.IP.prefilter(line,lnum>0))
449 newline('') # ensure a closing newline, needed by doctest
463 newline('') # ensure a closing newline, needed by doctest
450 #print "PYSRC:", '\n'.join(out) # dbg
464 #print "PYSRC:", '\n'.join(out) # dbg
451 return '\n'.join(out)
465 return '\n'.join(out)
452
466
453 def parse(self, string, name='<string>'):
467 def parse(self, string, name='<string>'):
454 """
468 """
455 Divide the given string into examples and intervening text,
469 Divide the given string into examples and intervening text,
456 and return them as a list of alternating Examples and strings.
470 and return them as a list of alternating Examples and strings.
457 Line numbers for the Examples are 0-based. The optional
471 Line numbers for the Examples are 0-based. The optional
458 argument `name` is a name identifying this string, and is only
472 argument `name` is a name identifying this string, and is only
459 used for error messages.
473 used for error messages.
460 """
474 """
461
475
462 #print 'Parse string:\n',string # dbg
476 #print 'Parse string:\n',string # dbg
463
477
464 string = string.expandtabs()
478 string = string.expandtabs()
465 # If all lines begin with the same indentation, then strip it.
479 # If all lines begin with the same indentation, then strip it.
466 min_indent = self._min_indent(string)
480 min_indent = self._min_indent(string)
467 if min_indent > 0:
481 if min_indent > 0:
468 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
482 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
469
483
470 output = []
484 output = []
471 charno, lineno = 0, 0
485 charno, lineno = 0, 0
472
486
473 # We make 'all random' tests by adding the '# random' mark to every
487 # We make 'all random' tests by adding the '# random' mark to every
474 # block of output in the test.
488 # block of output in the test.
475 if self._RANDOM_TEST.search(string):
489 if self._RANDOM_TEST.search(string):
476 random_marker = '\n# random'
490 random_marker = '\n# random'
477 else:
491 else:
478 random_marker = ''
492 random_marker = ''
479
493
480 # Whether to convert the input from ipython to python syntax
494 # Whether to convert the input from ipython to python syntax
481 ip2py = False
495 ip2py = False
482 # Find all doctest examples in the string. First, try them as Python
496 # Find all doctest examples in the string. First, try them as Python
483 # examples, then as IPython ones
497 # examples, then as IPython ones
484 terms = list(self._EXAMPLE_RE_PY.finditer(string))
498 terms = list(self._EXAMPLE_RE_PY.finditer(string))
485 if terms:
499 if terms:
486 # Normal Python example
500 # Normal Python example
487 #print '-'*70 # dbg
501 #print '-'*70 # dbg
488 #print 'PyExample, Source:\n',string # dbg
502 #print 'PyExample, Source:\n',string # dbg
489 #print '-'*70 # dbg
503 #print '-'*70 # dbg
490 Example = doctest.Example
504 Example = doctest.Example
491 else:
505 else:
492 # It's an ipython example. Note that IPExamples are run
506 # It's an ipython example. Note that IPExamples are run
493 # in-process, so their syntax must be turned into valid python.
507 # in-process, so their syntax must be turned into valid python.
494 # IPExternalExamples are run out-of-process (via pexpect) so they
508 # IPExternalExamples are run out-of-process (via pexpect) so they
495 # don't need any filtering (a real ipython will be executing them).
509 # don't need any filtering (a real ipython will be executing them).
496 terms = list(self._EXAMPLE_RE_IP.finditer(string))
510 terms = list(self._EXAMPLE_RE_IP.finditer(string))
497 if self._EXTERNAL_IP.search(string):
511 if self._EXTERNAL_IP.search(string):
498 #print '-'*70 # dbg
512 #print '-'*70 # dbg
499 #print 'IPExternalExample, Source:\n',string # dbg
513 #print 'IPExternalExample, Source:\n',string # dbg
500 #print '-'*70 # dbg
514 #print '-'*70 # dbg
501 Example = IPExternalExample
515 Example = IPExternalExample
502 else:
516 else:
503 #print '-'*70 # dbg
517 #print '-'*70 # dbg
504 #print 'IPExample, Source:\n',string # dbg
518 #print 'IPExample, Source:\n',string # dbg
505 #print '-'*70 # dbg
519 #print '-'*70 # dbg
506 Example = IPExample
520 Example = IPExample
507 ip2py = True
521 ip2py = True
508
522
509 for m in terms:
523 for m in terms:
510 # Add the pre-example text to `output`.
524 # Add the pre-example text to `output`.
511 output.append(string[charno:m.start()])
525 output.append(string[charno:m.start()])
512 # Update lineno (lines before this example)
526 # Update lineno (lines before this example)
513 lineno += string.count('\n', charno, m.start())
527 lineno += string.count('\n', charno, m.start())
514 # Extract info from the regexp match.
528 # Extract info from the regexp match.
515 (source, options, want, exc_msg) = \
529 (source, options, want, exc_msg) = \
516 self._parse_example(m, name, lineno,ip2py)
530 self._parse_example(m, name, lineno,ip2py)
517
531
518 # Append the random-output marker (it defaults to empty in most
532 # Append the random-output marker (it defaults to empty in most
519 # cases, it's only non-empty for 'all-random' tests):
533 # cases, it's only non-empty for 'all-random' tests):
520 want += random_marker
534 want += random_marker
521
535
522 if Example is IPExternalExample:
536 if Example is IPExternalExample:
523 options[doctest.NORMALIZE_WHITESPACE] = True
537 options[doctest.NORMALIZE_WHITESPACE] = True
524 want += '\n'
538 want += '\n'
525
539
526 # Create an Example, and add it to the list.
540 # Create an Example, and add it to the list.
527 if not self._IS_BLANK_OR_COMMENT(source):
541 if not self._IS_BLANK_OR_COMMENT(source):
528 output.append(Example(source, want, exc_msg,
542 output.append(Example(source, want, exc_msg,
529 lineno=lineno,
543 lineno=lineno,
530 indent=min_indent+len(m.group('indent')),
544 indent=min_indent+len(m.group('indent')),
531 options=options))
545 options=options))
532 # Update lineno (lines inside this example)
546 # Update lineno (lines inside this example)
533 lineno += string.count('\n', m.start(), m.end())
547 lineno += string.count('\n', m.start(), m.end())
534 # Update charno.
548 # Update charno.
535 charno = m.end()
549 charno = m.end()
536 # Add any remaining post-example text to `output`.
550 # Add any remaining post-example text to `output`.
537 output.append(string[charno:])
551 output.append(string[charno:])
538 return output
552 return output
539
553
540 def _parse_example(self, m, name, lineno,ip2py=False):
554 def _parse_example(self, m, name, lineno,ip2py=False):
541 """
555 """
542 Given a regular expression match from `_EXAMPLE_RE` (`m`),
556 Given a regular expression match from `_EXAMPLE_RE` (`m`),
543 return a pair `(source, want)`, where `source` is the matched
557 return a pair `(source, want)`, where `source` is the matched
544 example's source code (with prompts and indentation stripped);
558 example's source code (with prompts and indentation stripped);
545 and `want` is the example's expected output (with indentation
559 and `want` is the example's expected output (with indentation
546 stripped).
560 stripped).
547
561
548 `name` is the string's name, and `lineno` is the line number
562 `name` is the string's name, and `lineno` is the line number
549 where the example starts; both are used for error messages.
563 where the example starts; both are used for error messages.
550
564
551 Optional:
565 Optional:
552 `ip2py`: if true, filter the input via IPython to convert the syntax
566 `ip2py`: if true, filter the input via IPython to convert the syntax
553 into valid python.
567 into valid python.
554 """
568 """
555
569
556 # Get the example's indentation level.
570 # Get the example's indentation level.
557 indent = len(m.group('indent'))
571 indent = len(m.group('indent'))
558
572
559 # Divide source into lines; check that they're properly
573 # Divide source into lines; check that they're properly
560 # indented; and then strip their indentation & prompts.
574 # indented; and then strip their indentation & prompts.
561 source_lines = m.group('source').split('\n')
575 source_lines = m.group('source').split('\n')
562
576
563 # We're using variable-length input prompts
577 # We're using variable-length input prompts
564 ps1 = m.group('ps1')
578 ps1 = m.group('ps1')
565 ps2 = m.group('ps2')
579 ps2 = m.group('ps2')
566 ps1_len = len(ps1)
580 ps1_len = len(ps1)
567
581
568 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
582 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
569 if ps2:
583 if ps2:
570 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
584 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
571
585
572 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
586 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
573
587
574 if ip2py:
588 if ip2py:
575 # Convert source input from IPython into valid Python syntax
589 # Convert source input from IPython into valid Python syntax
576 source = self.ip2py(source)
590 source = self.ip2py(source)
577
591
578 # Divide want into lines; check that it's properly indented; and
592 # Divide want into lines; check that it's properly indented; and
579 # then strip the indentation. Spaces before the last newline should
593 # then strip the indentation. Spaces before the last newline should
580 # be preserved, so plain rstrip() isn't good enough.
594 # be preserved, so plain rstrip() isn't good enough.
581 want = m.group('want')
595 want = m.group('want')
582 want_lines = want.split('\n')
596 want_lines = want.split('\n')
583 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
597 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
584 del want_lines[-1] # forget final newline & spaces after it
598 del want_lines[-1] # forget final newline & spaces after it
585 self._check_prefix(want_lines, ' '*indent, name,
599 self._check_prefix(want_lines, ' '*indent, name,
586 lineno + len(source_lines))
600 lineno + len(source_lines))
587
601
588 # Remove ipython output prompt that might be present in the first line
602 # Remove ipython output prompt that might be present in the first line
589 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
603 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
590
604
591 want = '\n'.join([wl[indent:] for wl in want_lines])
605 want = '\n'.join([wl[indent:] for wl in want_lines])
592
606
593 # If `want` contains a traceback message, then extract it.
607 # If `want` contains a traceback message, then extract it.
594 m = self._EXCEPTION_RE.match(want)
608 m = self._EXCEPTION_RE.match(want)
595 if m:
609 if m:
596 exc_msg = m.group('msg')
610 exc_msg = m.group('msg')
597 else:
611 else:
598 exc_msg = None
612 exc_msg = None
599
613
600 # Extract options from the source.
614 # Extract options from the source.
601 options = self._find_options(source, name, lineno)
615 options = self._find_options(source, name, lineno)
602
616
603 return source, options, want, exc_msg
617 return source, options, want, exc_msg
604
618
605 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
619 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
606 """
620 """
607 Given the lines of a source string (including prompts and
621 Given the lines of a source string (including prompts and
608 leading indentation), check to make sure that every prompt is
622 leading indentation), check to make sure that every prompt is
609 followed by a space character. If any line is not followed by
623 followed by a space character. If any line is not followed by
610 a space character, then raise ValueError.
624 a space character, then raise ValueError.
611
625
612 Note: IPython-modified version which takes the input prompt length as a
626 Note: IPython-modified version which takes the input prompt length as a
613 parameter, so that prompts of variable length can be dealt with.
627 parameter, so that prompts of variable length can be dealt with.
614 """
628 """
615 space_idx = indent+ps1_len
629 space_idx = indent+ps1_len
616 min_len = space_idx+1
630 min_len = space_idx+1
617 for i, line in enumerate(lines):
631 for i, line in enumerate(lines):
618 if len(line) >= min_len and line[space_idx] != ' ':
632 if len(line) >= min_len and line[space_idx] != ' ':
619 raise ValueError('line %r of the docstring for %s '
633 raise ValueError('line %r of the docstring for %s '
620 'lacks blank after %s: %r' %
634 'lacks blank after %s: %r' %
621 (lineno+i+1, name,
635 (lineno+i+1, name,
622 line[indent:space_idx], line))
636 line[indent:space_idx], line))
623
637
624
638
625 SKIP = doctest.register_optionflag('SKIP')
639 SKIP = doctest.register_optionflag('SKIP')
626
640
627
641
628 class IPDocTestRunner(doctest.DocTestRunner,object):
642 class IPDocTestRunner(doctest.DocTestRunner,object):
629 """Test runner that synchronizes the IPython namespace with test globals.
643 """Test runner that synchronizes the IPython namespace with test globals.
630 """
644 """
631
645
632 def run(self, test, compileflags=None, out=None, clear_globs=True):
646 def run(self, test, compileflags=None, out=None, clear_globs=True):
633
647
634 # Hack: ipython needs access to the execution context of the example,
648 # Hack: ipython needs access to the execution context of the example,
635 # so that it can propagate user variables loaded by %run into
649 # so that it can propagate user variables loaded by %run into
636 # test.globs. We put them here into our modified %run as a function
650 # test.globs. We put them here into our modified %run as a function
637 # attribute. Our new %run will then only make the namespace update
651 # attribute. Our new %run will then only make the namespace update
638 # when called (rather than unconconditionally updating test.globs here
652 # when called (rather than unconconditionally updating test.globs here
639 # for all examples, most of which won't be calling %run anyway).
653 # for all examples, most of which won't be calling %run anyway).
640 _run_ns_sync.test_globs = test.globs
654 _run_ns_sync.test_globs = test.globs
641
655 _run_ns_sync.test_filename = test.filename
656
642 return super(IPDocTestRunner,self).run(test,
657 return super(IPDocTestRunner,self).run(test,
643 compileflags,out,clear_globs)
658 compileflags,out,clear_globs)
644
659
645
660
646 class DocFileCase(doctest.DocFileCase):
661 class DocFileCase(doctest.DocFileCase):
647 """Overrides to provide filename
662 """Overrides to provide filename
648 """
663 """
649 def address(self):
664 def address(self):
650 return (self._dt_test.filename, None, None)
665 return (self._dt_test.filename, None, None)
651
666
652
667
653 class ExtensionDoctest(doctests.Doctest):
668 class ExtensionDoctest(doctests.Doctest):
654 """Nose Plugin that supports doctests in extension modules.
669 """Nose Plugin that supports doctests in extension modules.
655 """
670 """
656 name = 'extdoctest' # call nosetests with --with-extdoctest
671 name = 'extdoctest' # call nosetests with --with-extdoctest
657 enabled = True
672 enabled = True
658
673
659 def options(self, parser, env=os.environ):
674 def options(self, parser, env=os.environ):
660 Plugin.options(self, parser, env)
675 Plugin.options(self, parser, env)
661 parser.add_option('--doctest-tests', action='store_true',
676 parser.add_option('--doctest-tests', action='store_true',
662 dest='doctest_tests',
677 dest='doctest_tests',
663 default=env.get('NOSE_DOCTEST_TESTS',True),
678 default=env.get('NOSE_DOCTEST_TESTS',True),
664 help="Also look for doctests in test modules. "
679 help="Also look for doctests in test modules. "
665 "Note that classes, methods and functions should "
680 "Note that classes, methods and functions should "
666 "have either doctests or non-doctest tests, "
681 "have either doctests or non-doctest tests, "
667 "not both. [NOSE_DOCTEST_TESTS]")
682 "not both. [NOSE_DOCTEST_TESTS]")
668 parser.add_option('--doctest-extension', action="append",
683 parser.add_option('--doctest-extension', action="append",
669 dest="doctestExtension",
684 dest="doctestExtension",
670 help="Also look for doctests in files with "
685 help="Also look for doctests in files with "
671 "this extension [NOSE_DOCTEST_EXTENSION]")
686 "this extension [NOSE_DOCTEST_EXTENSION]")
672 # Set the default as a list, if given in env; otherwise
687 # Set the default as a list, if given in env; otherwise
673 # an additional value set on the command line will cause
688 # an additional value set on the command line will cause
674 # an error.
689 # an error.
675 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
690 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
676 if env_setting is not None:
691 if env_setting is not None:
677 parser.set_defaults(doctestExtension=tolist(env_setting))
692 parser.set_defaults(doctestExtension=tolist(env_setting))
678
693
679
694
680 def configure(self, options, config):
695 def configure(self, options, config):
681 Plugin.configure(self, options, config)
696 Plugin.configure(self, options, config)
682 self.doctest_tests = options.doctest_tests
697 self.doctest_tests = options.doctest_tests
683 self.extension = tolist(options.doctestExtension)
698 self.extension = tolist(options.doctestExtension)
684
699
685 self.parser = doctest.DocTestParser()
700 self.parser = doctest.DocTestParser()
686 self.finder = DocTestFinder()
701 self.finder = DocTestFinder()
687 self.checker = IPDoctestOutputChecker()
702 self.checker = IPDoctestOutputChecker()
688 self.globs = None
703 self.globs = None
689 self.extraglobs = None
704 self.extraglobs = None
690
705
691
706
692 def loadTestsFromExtensionModule(self,filename):
707 def loadTestsFromExtensionModule(self,filename):
693 bpath,mod = os.path.split(filename)
708 bpath,mod = os.path.split(filename)
694 modname = os.path.splitext(mod)[0]
709 modname = os.path.splitext(mod)[0]
695 try:
710 try:
696 sys.path.append(bpath)
711 sys.path.append(bpath)
697 module = __import__(modname)
712 module = __import__(modname)
698 tests = list(self.loadTestsFromModule(module))
713 tests = list(self.loadTestsFromModule(module))
699 finally:
714 finally:
700 sys.path.pop()
715 sys.path.pop()
701 return tests
716 return tests
702
717
703 # NOTE: the method below is almost a copy of the original one in nose, with
718 # NOTE: the method below is almost a copy of the original one in nose, with
704 # a few modifications to control output checking.
719 # a few modifications to control output checking.
705
720
706 def loadTestsFromModule(self, module):
721 def loadTestsFromModule(self, module):
707 #print '*** ipdoctest - lTM',module # dbg
722 #print '*** ipdoctest - lTM',module # dbg
708
723
709 if not self.matches(module.__name__):
724 if not self.matches(module.__name__):
710 log.debug("Doctest doesn't want module %s", module)
725 log.debug("Doctest doesn't want module %s", module)
711 return
726 return
712
727
713 tests = self.finder.find(module,globs=self.globs,
728 tests = self.finder.find(module,globs=self.globs,
714 extraglobs=self.extraglobs)
729 extraglobs=self.extraglobs)
715 if not tests:
730 if not tests:
716 return
731 return
717
732
718 # always use whitespace and ellipsis options
733 # always use whitespace and ellipsis options
719 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
734 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
720
735
721 tests.sort()
736 tests.sort()
722 module_file = module.__file__
737 module_file = module.__file__
723 if module_file[-4:] in ('.pyc', '.pyo'):
738 if module_file[-4:] in ('.pyc', '.pyo'):
724 module_file = module_file[:-1]
739 module_file = module_file[:-1]
725 for test in tests:
740 for test in tests:
726 if not test.examples:
741 if not test.examples:
727 continue
742 continue
728 if not test.filename:
743 if not test.filename:
729 test.filename = module_file
744 test.filename = module_file
730
745
731 yield DocTestCase(test,
746 yield DocTestCase(test,
732 optionflags=optionflags,
747 optionflags=optionflags,
733 checker=self.checker)
748 checker=self.checker)
734
749
735
750
736 def loadTestsFromFile(self, filename):
751 def loadTestsFromFile(self, filename):
737 if is_extension_module(filename):
752 if is_extension_module(filename):
738 for t in self.loadTestsFromExtensionModule(filename):
753 for t in self.loadTestsFromExtensionModule(filename):
739 yield t
754 yield t
740 else:
755 else:
741 if self.extension and anyp(filename.endswith, self.extension):
756 if self.extension and anyp(filename.endswith, self.extension):
742 name = os.path.basename(filename)
757 name = os.path.basename(filename)
743 dh = open(filename)
758 dh = open(filename)
744 try:
759 try:
745 doc = dh.read()
760 doc = dh.read()
746 finally:
761 finally:
747 dh.close()
762 dh.close()
748 test = self.parser.get_doctest(
763 test = self.parser.get_doctest(
749 doc, globs={'__file__': filename}, name=name,
764 doc, globs={'__file__': filename}, name=name,
750 filename=filename, lineno=0)
765 filename=filename, lineno=0)
751 if test.examples:
766 if test.examples:
752 #print 'FileCase:',test.examples # dbg
767 #print 'FileCase:',test.examples # dbg
753 yield DocFileCase(test)
768 yield DocFileCase(test)
754 else:
769 else:
755 yield False # no tests to load
770 yield False # no tests to load
756
771
757 def wantFile(self,filename):
772 def wantFile(self,filename):
758 """Return whether the given filename should be scanned for tests.
773 """Return whether the given filename should be scanned for tests.
759
774
760 Modified version that accepts extension modules as valid containers for
775 Modified version that accepts extension modules as valid containers for
761 doctests.
776 doctests.
762 """
777 """
763 #print '*** ipdoctest- wantFile:',filename # dbg
778 #print '*** ipdoctest- wantFile:',filename # dbg
764
779
765 # XXX - temporarily hardcoded list, will move to driver later
780 # XXX - temporarily hardcoded list, will move to driver later
766 exclude = ['IPython/external/',
781 exclude = ['IPython/external/',
767 'IPython/platutils_win32',
782 'IPython/platutils_win32',
768 'IPython/frontend/cocoa',
783 'IPython/frontend/cocoa',
769 'IPython_doctest_plugin',
784 'IPython_doctest_plugin',
770 'IPython/Gnuplot',
785 'IPython/Gnuplot',
771 'IPython/Extensions/ipy_',
786 'IPython/Extensions/ipy_',
772 'IPython/Extensions/PhysicalQIn',
787 'IPython/Extensions/PhysicalQIn',
773 'IPython/Extensions/scitedirector',
788 'IPython/Extensions/scitedirector',
774 'IPython/testing/plugin',
789 'IPython/testing/plugin',
775 ]
790 ]
776
791
777 for fex in exclude:
792 for fex in exclude:
778 if fex in filename: # substring
793 if fex in filename: # substring
779 #print '###>>> SKIP:',filename # dbg
794 #print '###>>> SKIP:',filename # dbg
780 return False
795 return False
781
796
782 if is_extension_module(filename):
797 if is_extension_module(filename):
783 return True
798 return True
784 else:
799 else:
785 return doctests.Doctest.wantFile(self,filename)
800 return doctests.Doctest.wantFile(self,filename)
786
801
787
802
788 class IPythonDoctest(ExtensionDoctest):
803 class IPythonDoctest(ExtensionDoctest):
789 """Nose Plugin that supports doctests in extension modules.
804 """Nose Plugin that supports doctests in extension modules.
790 """
805 """
791 name = 'ipdoctest' # call nosetests with --with-ipdoctest
806 name = 'ipdoctest' # call nosetests with --with-ipdoctest
792 enabled = True
807 enabled = True
793
808
794 def configure(self, options, config):
809 def configure(self, options, config):
795
810
796 Plugin.configure(self, options, config)
811 Plugin.configure(self, options, config)
797 self.doctest_tests = options.doctest_tests
812 self.doctest_tests = options.doctest_tests
798 self.extension = tolist(options.doctestExtension)
813 self.extension = tolist(options.doctestExtension)
799
814
800 self.parser = IPDocTestParser()
815 self.parser = IPDocTestParser()
801 self.finder = DocTestFinder(parser=self.parser)
816 self.finder = DocTestFinder(parser=self.parser)
802 self.checker = IPDoctestOutputChecker()
817 self.checker = IPDoctestOutputChecker()
803 self.globs = None
818 self.globs = None
804 self.extraglobs = None
819 self.extraglobs = None
805
820
@@ -1,21 +1,92 b''
1 """ Tests for various magic functions
1 """ Tests for various magic functions
2
2
3 Needs to be run by nose (to make ipython session available)
3 Needs to be run by nose (to make ipython session available)
4
5 """
4 """
5
6 from IPython.testing import decorators as dec
7
6 def test_rehashx():
8 def test_rehashx():
7 # clear up everything
9 # clear up everything
8 _ip.IP.alias_table.clear()
10 _ip.IP.alias_table.clear()
9 del _ip.db['syscmdlist']
11 del _ip.db['syscmdlist']
10
12
11 _ip.magic('rehashx')
13 _ip.magic('rehashx')
12 # Practically ALL ipython development systems will have more than 10 aliases
14 # Practically ALL ipython development systems will have more than 10 aliases
13
15
14 assert len(_ip.IP.alias_table) > 10
16 assert len(_ip.IP.alias_table) > 10
15 for key, val in _ip.IP.alias_table.items():
17 for key, val in _ip.IP.alias_table.items():
16 # we must strip dots from alias names
18 # we must strip dots from alias names
17 assert '.' not in key
19 assert '.' not in key
18
20
19 # rehashx must fill up syscmdlist
21 # rehashx must fill up syscmdlist
20 scoms = _ip.db['syscmdlist']
22 scoms = _ip.db['syscmdlist']
21 assert len(scoms) > 10
23 assert len(scoms) > 10
24
25
26 def doctest_run_ns():
27 """Classes declared %run scripts must be instantiable afterwards.
28
29 In [3]: run tclass.py
30
31 In [4]: f()
32 """
33 pass # doctest only
34
35
36 def doctest_hist_f():
37 """Test %hist -f with temporary filename.
38
39 In [9]: import tempfile
40
41 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
42
43 In [11]: %history -n -f $tfile 3
44 """
45
46
47 def doctest_hist_r():
48 """Test %hist -r
49
50 XXX - This test is not recording the output correctly. Not sure why...
51
52 In [6]: x=1
53
54 In [7]: hist -n -r 2
55 x=1 # random
56 hist -n -r 2 # random
57 """
58
59
60 def test_shist():
61 # Simple tests of ShadowHist class
62 import os, shutil, tempfile
63 import nose.tools as nt
64
65 from IPython.Extensions import pickleshare
66 from IPython.history import ShadowHist
67
68
69 tfile = tempfile.mktemp('','tmp-ipython-')
70
71 db = pickleshare.PickleShareDB(tfile)
72 s = ShadowHist(db)
73 s.add('hello')
74 s.add('world')
75 s.add('hello')
76 s.add('hello')
77 s.add('karhu')
78
79 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
80
81 yield nt.assert_equal,s.get(2),'world'
82
83 shutil.rmtree(tfile)
84
85
86 @dec.skip_numpy_not_avail
87 def doctest_clear_array():
88 """Check that array clearing works.
89
90 >>> 1/0
91 """
92 pass # doctest only
General Comments 0
You need to be logged in to leave comments. Login now