##// END OF EJS Templates
don't instantiate IPython shell as class attr...
MinRK -
Show More
@@ -1,828 +1,829 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Sphinx directive to support embedded IPython code.
3 3
4 4 This directive allows pasting of entire interactive IPython sessions, prompts
5 5 and all, and their code will actually get re-executed at doc build time, with
6 6 all prompts renumbered sequentially. It also allows you to input code as a pure
7 7 python input by giving the argument python to the directive. The output looks
8 8 like an interactive ipython section.
9 9
10 10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
11 11 (making sure the directory where you placed it is visible to sphinx, as is
12 12 needed for all Sphinx directives).
13 13
14 14 By default this directive assumes that your prompts are unchanged IPython ones,
15 15 but this can be customized. The configurable options that can be placed in
16 16 conf.py are
17 17
18 18 ipython_savefig_dir:
19 19 The directory in which to save the figures. This is relative to the
20 20 Sphinx source directory. The default is `html_static_path`.
21 21 ipython_rgxin:
22 22 The compiled regular expression to denote the start of IPython input
23 23 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
24 24 shouldn't need to change this.
25 25 ipython_rgxout:
26 26 The compiled regular expression to denote the start of IPython output
27 27 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
28 28 shouldn't need to change this.
29 29 ipython_promptin:
30 30 The string to represent the IPython input prompt in the generated ReST.
31 31 The default is 'In [%d]:'. This expects that the line numbers are used
32 32 in the prompt.
33 33 ipython_promptout:
34 34
35 35 The string to represent the IPython prompt in the generated ReST. The
36 36 default is 'Out [%d]:'. This expects that the line numbers are used
37 37 in the prompt.
38 38
39 39 ToDo
40 40 ----
41 41
42 42 - Turn the ad-hoc test() function into a real test suite.
43 43 - Break up ipython-specific functionality from matplotlib stuff into better
44 44 separated code.
45 45
46 46 Authors
47 47 -------
48 48
49 49 - John D Hunter: orignal author.
50 50 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
51 51 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
52 52 - Skipper Seabold, refactoring, cleanups, pure python addition
53 53 """
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Imports
57 57 #-----------------------------------------------------------------------------
58 58
59 59 # Stdlib
60 60 import cStringIO
61 61 import os
62 62 import re
63 63 import sys
64 64 import tempfile
65 65 import ast
66 66
67 67 # To keep compatibility with various python versions
68 68 try:
69 69 from hashlib import md5
70 70 except ImportError:
71 71 from md5 import md5
72 72
73 73 # Third-party
74 74 import matplotlib
75 75 import sphinx
76 76 from docutils.parsers.rst import directives
77 77 from docutils import nodes
78 78 from sphinx.util.compat import Directive
79 79
80 80 matplotlib.use('Agg')
81 81
82 82 # Our own
83 83 from IPython import Config, InteractiveShell
84 84 from IPython.core.profiledir import ProfileDir
85 85 from IPython.utils import io
86 86
87 87 #-----------------------------------------------------------------------------
88 88 # Globals
89 89 #-----------------------------------------------------------------------------
90 90 # for tokenizing blocks
91 91 COMMENT, INPUT, OUTPUT = range(3)
92 92
93 93 #-----------------------------------------------------------------------------
94 94 # Functions and class declarations
95 95 #-----------------------------------------------------------------------------
96 96 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
97 97 """
98 98 part is a string of ipython text, comprised of at most one
99 99 input, one ouput, comments, and blank lines. The block parser
100 100 parses the text into a list of::
101 101
102 102 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
103 103
104 104 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
105 105 data is, depending on the type of token::
106 106
107 107 COMMENT : the comment string
108 108
109 109 INPUT: the (DECORATOR, INPUT_LINE, REST) where
110 110 DECORATOR: the input decorator (or None)
111 111 INPUT_LINE: the input as string (possibly multi-line)
112 112 REST : any stdout generated by the input line (not OUTPUT)
113 113
114 114
115 115 OUTPUT: the output string, possibly multi-line
116 116 """
117 117
118 118 block = []
119 119 lines = part.split('\n')
120 120 N = len(lines)
121 121 i = 0
122 122 decorator = None
123 123 while 1:
124 124
125 125 if i==N:
126 126 # nothing left to parse -- the last line
127 127 break
128 128
129 129 line = lines[i]
130 130 i += 1
131 131 line_stripped = line.strip()
132 132 if line_stripped.startswith('#'):
133 133 block.append((COMMENT, line))
134 134 continue
135 135
136 136 if line_stripped.startswith('@'):
137 137 # we're assuming at most one decorator -- may need to
138 138 # rethink
139 139 decorator = line_stripped
140 140 continue
141 141
142 142 # does this look like an input line?
143 143 matchin = rgxin.match(line)
144 144 if matchin:
145 145 lineno, inputline = int(matchin.group(1)), matchin.group(2)
146 146
147 147 # the ....: continuation string
148 148 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
149 149 Nc = len(continuation)
150 150 # input lines can continue on for more than one line, if
151 151 # we have a '\' line continuation char or a function call
152 152 # echo line 'print'. The input line can only be
153 153 # terminated by the end of the block or an output line, so
154 154 # we parse out the rest of the input line if it is
155 155 # multiline as well as any echo text
156 156
157 157 rest = []
158 158 while i<N:
159 159
160 160 # look ahead; if the next line is blank, or a comment, or
161 161 # an output line, we're done
162 162
163 163 nextline = lines[i]
164 164 matchout = rgxout.match(nextline)
165 165 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
166 166 if matchout or nextline.startswith('#'):
167 167 break
168 168 elif nextline.startswith(continuation):
169 169 inputline += '\n' + nextline[Nc:]
170 170 else:
171 171 rest.append(nextline)
172 172 i+= 1
173 173
174 174 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
175 175 continue
176 176
177 177 # if it looks like an output line grab all the text to the end
178 178 # of the block
179 179 matchout = rgxout.match(line)
180 180 if matchout:
181 181 lineno, output = int(matchout.group(1)), matchout.group(2)
182 182 if i<N-1:
183 183 output = '\n'.join([output] + lines[i:])
184 184
185 185 block.append((OUTPUT, output))
186 186 break
187 187
188 188 return block
189 189
190 190 class EmbeddedSphinxShell(object):
191 191 """An embedded IPython instance to run inside Sphinx"""
192 192
193 193 def __init__(self):
194 194
195 195 self.cout = cStringIO.StringIO()
196 196
197 197
198 198 # Create config object for IPython
199 199 config = Config()
200 200 config.Global.display_banner = False
201 201 config.Global.exec_lines = ['import numpy as np',
202 202 'from pylab import *'
203 203 ]
204 204 config.InteractiveShell.autocall = False
205 205 config.InteractiveShell.autoindent = False
206 206 config.InteractiveShell.colors = 'NoColor'
207 207
208 208 # create a profile so instance history isn't saved
209 209 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
210 210 profname = 'auto_profile_sphinx_build'
211 211 pdir = os.path.join(tmp_profile_dir,profname)
212 212 profile = ProfileDir.create_profile_dir(pdir)
213 213
214 214 # Create and initialize ipython, but don't start its mainloop
215 215 IP = InteractiveShell.instance(config=config, profile_dir=profile)
216 216 # io.stdout redirect must be done *after* instantiating InteractiveShell
217 217 io.stdout = self.cout
218 218 io.stderr = self.cout
219 219
220 220 # For debugging, so we can see normal output, use this:
221 221 #from IPython.utils.io import Tee
222 222 #io.stdout = Tee(self.cout, channel='stdout') # dbg
223 223 #io.stderr = Tee(self.cout, channel='stderr') # dbg
224 224
225 225 # Store a few parts of IPython we'll need.
226 226 self.IP = IP
227 227 self.user_ns = self.IP.user_ns
228 228 self.user_global_ns = self.IP.user_global_ns
229 229
230 230 self.input = ''
231 231 self.output = ''
232 232
233 233 self.is_verbatim = False
234 234 self.is_doctest = False
235 235 self.is_suppress = False
236 236
237 237 # on the first call to the savefig decorator, we'll import
238 238 # pyplot as plt so we can make a call to the plt.gcf().savefig
239 239 self._pyplot_imported = False
240 240
241 241 def clear_cout(self):
242 242 self.cout.seek(0)
243 243 self.cout.truncate(0)
244 244
245 245 def process_input_line(self, line, store_history=True):
246 246 """process the input, capturing stdout"""
247 247 #print "input='%s'"%self.input
248 248 stdout = sys.stdout
249 249 splitter = self.IP.input_splitter
250 250 try:
251 251 sys.stdout = self.cout
252 252 splitter.push(line)
253 253 more = splitter.push_accepts_more()
254 254 if not more:
255 255 source_raw = splitter.source_raw_reset()[1]
256 256 self.IP.run_cell(source_raw, store_history=store_history)
257 257 finally:
258 258 sys.stdout = stdout
259 259
260 260 def process_image(self, decorator):
261 261 """
262 262 # build out an image directive like
263 263 # .. image:: somefile.png
264 264 # :width 4in
265 265 #
266 266 # from an input like
267 267 # savefig somefile.png width=4in
268 268 """
269 269 savefig_dir = self.savefig_dir
270 270 source_dir = self.source_dir
271 271 saveargs = decorator.split(' ')
272 272 filename = saveargs[1]
273 273 # insert relative path to image file in source
274 274 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
275 275 source_dir)
276 276
277 277 imagerows = ['.. image:: %s'%outfile]
278 278
279 279 for kwarg in saveargs[2:]:
280 280 arg, val = kwarg.split('=')
281 281 arg = arg.strip()
282 282 val = val.strip()
283 283 imagerows.append(' :%s: %s'%(arg, val))
284 284
285 285 image_file = os.path.basename(outfile) # only return file name
286 286 image_directive = '\n'.join(imagerows)
287 287 return image_file, image_directive
288 288
289 289
290 290 # Callbacks for each type of token
291 291 def process_input(self, data, input_prompt, lineno):
292 292 """Process data block for INPUT token."""
293 293 decorator, input, rest = data
294 294 image_file = None
295 295 image_directive = None
296 296 #print 'INPUT:', data # dbg
297 297 is_verbatim = decorator=='@verbatim' or self.is_verbatim
298 298 is_doctest = decorator=='@doctest' or self.is_doctest
299 299 is_suppress = decorator=='@suppress' or self.is_suppress
300 300 is_savefig = decorator is not None and \
301 301 decorator.startswith('@savefig')
302 302
303 303 input_lines = input.split('\n')
304 304 if len(input_lines) > 1:
305 305 if input_lines[-1] != "":
306 306 input_lines.append('') # make sure there's a blank line
307 307 # so splitter buffer gets reset
308 308
309 309 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
310 310 Nc = len(continuation)
311 311
312 312 if is_savefig:
313 313 image_file, image_directive = self.process_image(decorator)
314 314
315 315 ret = []
316 316 is_semicolon = False
317 317
318 318 for i, line in enumerate(input_lines):
319 319 if line.endswith(';'):
320 320 is_semicolon = True
321 321
322 322 if i==0:
323 323 # process the first input line
324 324 if is_verbatim:
325 325 self.process_input_line('')
326 326 self.IP.execution_count += 1 # increment it anyway
327 327 else:
328 328 # only submit the line in non-verbatim mode
329 329 self.process_input_line(line, store_history=True)
330 330 formatted_line = '%s %s'%(input_prompt, line)
331 331 else:
332 332 # process a continuation line
333 333 if not is_verbatim:
334 334 self.process_input_line(line, store_history=True)
335 335
336 336 formatted_line = '%s %s'%(continuation, line)
337 337
338 338 if not is_suppress:
339 339 ret.append(formatted_line)
340 340
341 341 if not is_suppress and len(rest.strip()) and is_verbatim:
342 342 # the "rest" is the standard output of the
343 343 # input, which needs to be added in
344 344 # verbatim mode
345 345 ret.append(rest)
346 346
347 347 self.cout.seek(0)
348 348 output = self.cout.read()
349 349 if not is_suppress and not is_semicolon:
350 350 ret.append(output)
351 351 elif is_semicolon: # get spacing right
352 352 ret.append('')
353 353
354 354 self.cout.truncate(0)
355 355 return (ret, input_lines, output, is_doctest, image_file,
356 356 image_directive)
357 357 #print 'OUTPUT', output # dbg
358 358
359 359 def process_output(self, data, output_prompt,
360 360 input_lines, output, is_doctest, image_file):
361 361 """Process data block for OUTPUT token."""
362 362 if is_doctest:
363 363 submitted = data.strip()
364 364 found = output
365 365 if found is not None:
366 366 found = found.strip()
367 367
368 368 # XXX - fperez: in 0.11, 'output' never comes with the prompt
369 369 # in it, just the actual output text. So I think all this code
370 370 # can be nuked...
371 371
372 372 # the above comment does not appear to be accurate... (minrk)
373 373
374 374 ind = found.find(output_prompt)
375 375 if ind<0:
376 376 e='output prompt="%s" does not match out line=%s' % \
377 377 (output_prompt, found)
378 378 raise RuntimeError(e)
379 379 found = found[len(output_prompt):].strip()
380 380
381 381 if found!=submitted:
382 382 e = ('doctest failure for input_lines="%s" with '
383 383 'found_output="%s" and submitted output="%s"' %
384 384 (input_lines, found, submitted) )
385 385 raise RuntimeError(e)
386 386 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
387 387
388 388 def process_comment(self, data):
389 389 """Process data fPblock for COMMENT token."""
390 390 if not self.is_suppress:
391 391 return [data]
392 392
393 393 def save_image(self, image_file):
394 394 """
395 395 Saves the image file to disk.
396 396 """
397 397 self.ensure_pyplot()
398 398 command = 'plt.gcf().savefig("%s")'%image_file
399 399 #print 'SAVEFIG', command # dbg
400 400 self.process_input_line('bookmark ipy_thisdir', store_history=False)
401 401 self.process_input_line('cd -b ipy_savedir', store_history=False)
402 402 self.process_input_line(command, store_history=False)
403 403 self.process_input_line('cd -b ipy_thisdir', store_history=False)
404 404 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
405 405 self.clear_cout()
406 406
407 407
408 408 def process_block(self, block):
409 409 """
410 410 process block from the block_parser and return a list of processed lines
411 411 """
412 412 ret = []
413 413 output = None
414 414 input_lines = None
415 415 lineno = self.IP.execution_count
416 416
417 417 input_prompt = self.promptin%lineno
418 418 output_prompt = self.promptout%lineno
419 419 image_file = None
420 420 image_directive = None
421 421
422 422 for token, data in block:
423 423 if token==COMMENT:
424 424 out_data = self.process_comment(data)
425 425 elif token==INPUT:
426 426 (out_data, input_lines, output, is_doctest, image_file,
427 427 image_directive) = \
428 428 self.process_input(data, input_prompt, lineno)
429 429 elif token==OUTPUT:
430 430 out_data = \
431 431 self.process_output(data, output_prompt,
432 432 input_lines, output, is_doctest,
433 433 image_file)
434 434 if out_data:
435 435 ret.extend(out_data)
436 436
437 437 # save the image files
438 438 if image_file is not None:
439 439 self.save_image(image_file)
440 440
441 441 return ret, image_directive
442 442
443 443 def ensure_pyplot(self):
444 444 if self._pyplot_imported:
445 445 return
446 446 self.process_input_line('import matplotlib.pyplot as plt',
447 447 store_history=False)
448 448
449 449 def process_pure_python(self, content):
450 450 """
451 451 content is a list of strings. it is unedited directive conent
452 452
453 453 This runs it line by line in the InteractiveShell, prepends
454 454 prompts as needed capturing stderr and stdout, then returns
455 455 the content as a list as if it were ipython code
456 456 """
457 457 output = []
458 458 savefig = False # keep up with this to clear figure
459 459 multiline = False # to handle line continuation
460 460 multiline_start = None
461 461 fmtin = self.promptin
462 462
463 463 ct = 0
464 464
465 465 for lineno, line in enumerate(content):
466 466
467 467 line_stripped = line.strip()
468 468 if not len(line):
469 469 output.append(line)
470 470 continue
471 471
472 472 # handle decorators
473 473 if line_stripped.startswith('@'):
474 474 output.extend([line])
475 475 if 'savefig' in line:
476 476 savefig = True # and need to clear figure
477 477 continue
478 478
479 479 # handle comments
480 480 if line_stripped.startswith('#'):
481 481 output.extend([line])
482 482 continue
483 483
484 484 # deal with lines checking for multiline
485 485 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
486 486 if not multiline:
487 487 modified = u"%s %s" % (fmtin % ct, line_stripped)
488 488 output.append(modified)
489 489 ct += 1
490 490 try:
491 491 ast.parse(line_stripped)
492 492 output.append(u'')
493 493 except Exception: # on a multiline
494 494 multiline = True
495 495 multiline_start = lineno
496 496 else: # still on a multiline
497 497 modified = u'%s %s' % (continuation, line)
498 498 output.append(modified)
499 499
500 500 # if the next line is indented, it should be part of multiline
501 501 if len(content) > lineno + 1:
502 502 nextline = content[lineno + 1]
503 503 if len(nextline) - len(nextline.lstrip()) > 3:
504 504 continue
505 505 try:
506 506 mod = ast.parse(
507 507 '\n'.join(content[multiline_start:lineno+1]))
508 508 if isinstance(mod.body[0], ast.FunctionDef):
509 509 # check to see if we have the whole function
510 510 for element in mod.body[0].body:
511 511 if isinstance(element, ast.Return):
512 512 multiline = False
513 513 else:
514 514 output.append(u'')
515 515 multiline = False
516 516 except Exception:
517 517 pass
518 518
519 519 if savefig: # clear figure if plotted
520 520 self.ensure_pyplot()
521 521 self.process_input_line('plt.clf()', store_history=False)
522 522 self.clear_cout()
523 523 savefig = False
524 524
525 525 return output
526 526
527 class IpythonDirective(Directive):
527 class IPythonDirective(Directive):
528 528
529 529 has_content = True
530 530 required_arguments = 0
531 531 optional_arguments = 4 # python, suppress, verbatim, doctest
532 532 final_argumuent_whitespace = True
533 533 option_spec = { 'python': directives.unchanged,
534 534 'suppress' : directives.flag,
535 535 'verbatim' : directives.flag,
536 536 'doctest' : directives.flag,
537 537 }
538 538
539 shell = EmbeddedSphinxShell()
539 shell = None
540 540
541 541 seen_docs = set()
542 542
543 543 def get_config_options(self):
544 544 # contains sphinx configuration variables
545 545 config = self.state.document.settings.env.config
546 546
547 547 # get config variables to set figure output directory
548 548 confdir = self.state.document.settings.env.app.confdir
549 549 savefig_dir = config.ipython_savefig_dir
550 550 source_dir = os.path.dirname(self.state.document.current_source)
551 551 if savefig_dir is None:
552 552 savefig_dir = config.html_static_path
553 553 if isinstance(savefig_dir, list):
554 554 savefig_dir = savefig_dir[0] # safe to assume only one path?
555 555 savefig_dir = os.path.join(confdir, savefig_dir)
556 556
557 557 # get regex and prompt stuff
558 558 rgxin = config.ipython_rgxin
559 559 rgxout = config.ipython_rgxout
560 560 promptin = config.ipython_promptin
561 561 promptout = config.ipython_promptout
562 562
563 563 return savefig_dir, source_dir, rgxin, rgxout, promptin, promptout
564 564
565 565 def setup(self):
566 if self.shell is None:
567 self.shell = EmbeddedSphinxShell()
566 568 # reset the execution count if we haven't processed this doc
567 569 #NOTE: this may be borked if there are multiple seen_doc tmp files
568 570 #check time stamp?
569 571
570
571 572 if not self.state.document.current_source in self.seen_docs:
572 573 self.shell.IP.history_manager.reset()
573 574 self.shell.IP.execution_count = 1
574 575 self.seen_docs.add(self.state.document.current_source)
575 576
576 577
577 578
578 579 # get config values
579 580 (savefig_dir, source_dir, rgxin,
580 581 rgxout, promptin, promptout) = self.get_config_options()
581 582
582 583 # and attach to shell so we don't have to pass them around
583 584 self.shell.rgxin = rgxin
584 585 self.shell.rgxout = rgxout
585 586 self.shell.promptin = promptin
586 587 self.shell.promptout = promptout
587 588 self.shell.savefig_dir = savefig_dir
588 589 self.shell.source_dir = source_dir
589 590
590 591 # setup bookmark for saving figures directory
591 592
592 593 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
593 594 store_history=False)
594 595 self.shell.clear_cout()
595 596
596 597 return rgxin, rgxout, promptin, promptout
597 598
598 599
599 600 def teardown(self):
600 601 # delete last bookmark
601 602 self.shell.process_input_line('bookmark -d ipy_savedir',
602 603 store_history=False)
603 604 self.shell.clear_cout()
604 605
605 606 def run(self):
606 607 debug = False
607 608
608 609 #TODO, any reason block_parser can't be a method of embeddable shell
609 610 # then we wouldn't have to carry these around
610 611 rgxin, rgxout, promptin, promptout = self.setup()
611 612
612 613 options = self.options
613 614 self.shell.is_suppress = 'suppress' in options
614 615 self.shell.is_doctest = 'doctest' in options
615 616 self.shell.is_verbatim = 'verbatim' in options
616 617
617 618
618 619 # handle pure python code
619 620 if 'python' in self.arguments:
620 621 content = self.content
621 622 self.content = self.shell.process_pure_python(content)
622 623
623 624 parts = '\n'.join(self.content).split('\n\n')
624 625
625 626 lines = ['.. code-block:: ipython','']
626 627 figures = []
627 628
628 629 for part in parts:
629 630
630 631 block = block_parser(part, rgxin, rgxout, promptin, promptout)
631 632
632 633 if len(block):
633 634 rows, figure = self.shell.process_block(block)
634 635 for row in rows:
635 636 lines.extend([' %s'%line for line in row.split('\n')])
636 637
637 638 if figure is not None:
638 639 figures.append(figure)
639 640
640 641 #text = '\n'.join(lines)
641 642 #figs = '\n'.join(figures)
642 643
643 644 for figure in figures:
644 645 lines.append('')
645 646 lines.extend(figure.split('\n'))
646 647 lines.append('')
647 648
648 649 #print lines
649 650 if len(lines)>2:
650 651 if debug:
651 652 print '\n'.join(lines)
652 653 else: #NOTE: this raises some errors, what's it for?
653 654 #print 'INSERTING %d lines'%len(lines)
654 655 self.state_machine.insert_input(
655 656 lines, self.state_machine.input_lines.source(0))
656 657
657 658 text = '\n'.join(lines)
658 659 txtnode = nodes.literal_block(text, text)
659 660 txtnode['language'] = 'ipython'
660 661 #imgnode = nodes.image(figs)
661 662
662 663 # cleanup
663 664 self.teardown()
664 665
665 666 return []#, imgnode]
666 667
667 668 # Enable as a proper Sphinx directive
668 669 def setup(app):
669 670 setup.app = app
670 671
671 app.add_directive('ipython', IpythonDirective)
672 app.add_directive('ipython', IPythonDirective)
672 673 app.add_config_value('ipython_savefig_dir', None, True)
673 674 app.add_config_value('ipython_rgxin',
674 675 re.compile('In \[(\d+)\]:\s?(.*)\s*'), True)
675 676 app.add_config_value('ipython_rgxout',
676 677 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), True)
677 678 app.add_config_value('ipython_promptin', 'In [%d]:', True)
678 679 app.add_config_value('ipython_promptout', 'Out[%d]:', True)
679 680
680 681
681 682 # Simple smoke test, needs to be converted to a proper automatic test.
682 683 def test():
683 684
684 685 examples = [
685 686 r"""
686 687 In [9]: pwd
687 688 Out[9]: '/home/jdhunter/py4science/book'
688 689
689 690 In [10]: cd bookdata/
690 691 /home/jdhunter/py4science/book/bookdata
691 692
692 693 In [2]: from pylab import *
693 694
694 695 In [2]: ion()
695 696
696 697 In [3]: im = imread('stinkbug.png')
697 698
698 699 @savefig mystinkbug.png width=4in
699 700 In [4]: imshow(im)
700 701 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
701 702
702 703 """,
703 704 r"""
704 705
705 706 In [1]: x = 'hello world'
706 707
707 708 # string methods can be
708 709 # used to alter the string
709 710 @doctest
710 711 In [2]: x.upper()
711 712 Out[2]: 'HELLO WORLD'
712 713
713 714 @verbatim
714 715 In [3]: x.st<TAB>
715 716 x.startswith x.strip
716 717 """,
717 718 r"""
718 719
719 720 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
720 721 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
721 722
722 723 In [131]: print url.split('&')
723 724 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
724 725
725 726 In [60]: import urllib
726 727
727 728 """,
728 729 r"""\
729 730
730 731 In [133]: import numpy.random
731 732
732 733 @suppress
733 734 In [134]: numpy.random.seed(2358)
734 735
735 736 @doctest
736 737 In [135]: numpy.random.rand(10,2)
737 738 Out[135]:
738 739 array([[ 0.64524308, 0.59943846],
739 740 [ 0.47102322, 0.8715456 ],
740 741 [ 0.29370834, 0.74776844],
741 742 [ 0.99539577, 0.1313423 ],
742 743 [ 0.16250302, 0.21103583],
743 744 [ 0.81626524, 0.1312433 ],
744 745 [ 0.67338089, 0.72302393],
745 746 [ 0.7566368 , 0.07033696],
746 747 [ 0.22591016, 0.77731835],
747 748 [ 0.0072729 , 0.34273127]])
748 749
749 750 """,
750 751
751 752 r"""
752 753 In [106]: print x
753 754 jdh
754 755
755 756 In [109]: for i in range(10):
756 757 .....: print i
757 758 .....:
758 759 .....:
759 760 0
760 761 1
761 762 2
762 763 3
763 764 4
764 765 5
765 766 6
766 767 7
767 768 8
768 769 9
769 770 """,
770 771
771 772 r"""
772 773
773 774 In [144]: from pylab import *
774 775
775 776 In [145]: ion()
776 777
777 778 # use a semicolon to suppress the output
778 779 @savefig test_hist.png width=4in
779 780 In [151]: hist(np.random.randn(10000), 100);
780 781
781 782
782 783 @savefig test_plot.png width=4in
783 784 In [151]: plot(np.random.randn(10000), 'o');
784 785 """,
785 786
786 787 r"""
787 788 # use a semicolon to suppress the output
788 789 In [151]: plt.clf()
789 790
790 791 @savefig plot_simple.png width=4in
791 792 In [151]: plot([1,2,3])
792 793
793 794 @savefig hist_simple.png width=4in
794 795 In [151]: hist(np.random.randn(10000), 100);
795 796
796 797 """,
797 798 r"""
798 799 # update the current fig
799 800 In [151]: ylabel('number')
800 801
801 802 In [152]: title('normal distribution')
802 803
803 804
804 805 @savefig hist_with_text.png
805 806 In [153]: grid(True)
806 807
807 808 """,
808 809 ]
809 810 # skip local-file depending first example:
810 811 examples = examples[1:]
811 812
812 813 #ipython_directive.DEBUG = True # dbg
813 814 #options = dict(suppress=True) # dbg
814 815 options = dict()
815 816 for example in examples:
816 817 content = example.split('\n')
817 ipython_directive('debug', arguments=None, options=options,
818 IPythonDirective('debug', arguments=None, options=options,
818 819 content=content, lineno=0,
819 820 content_offset=None, block_text=None,
820 821 state=None, state_machine=None,
821 822 )
822 823
823 824 # Run test suite as a script
824 825 if __name__=='__main__':
825 826 if not os.path.isdir('_static'):
826 827 os.mkdir('_static')
827 828 test()
828 829 print 'All OK? Check figures in _static/'
General Comments 0
You need to be logged in to leave comments. Login now